/*
 * @Author: wwssaabb
 * @Date: 2021-09-04 11:22:27
 * @LastEditTime: 2021-12-18 16:38:32
 * @FilePath: \handwritten-code\main\deepClone.js
 */
/* 
  深拷贝
  什么是浅拷贝、深拷贝、以及他们的区别？
    浅拷贝：新建一个对象对原对象的属性进行拷贝，不考虑引用类型，
      所以当新的对象进行修改时原对象也会被修改
    深拷贝：和浅拷贝的区别在于，对引用类型的属性会开辟内存来存
      放，拷贝的新对象修改时不会对原对象造成影响
    区别：浅拷贝拷贝的是内存地址，修改时会影响原对象，
      深拷贝完全开辟内存保存每一个属性值，修改时不会影响原对象
*/

// function deepClone(obj) {
//   //非对象以及function直接返回
//   if (typeof obj !== 'object' || obj === null) {
//     return obj
//   }
//   return Array.isArray(obj) ? obj.map(i => deepClone(i)) :
//     Object.entries(obj).reduce((p, c) => {
//       if (obj.hasOwnProperty(c[0])) {
//         p[c[0]] = deepClone(c[1])
//       }
//       return p
//     }, {})
// }
/*
function isObject(data) {
  return (typeof data === 'object' && data !== null) || typeof data === 'function'
}

function getType(data) {
  return Object.prototype.toString.call(data).slice(8, -1)
}


/* 
  常用数据类型：Map、Set、Symbol、RegExp、Date、Array、Object、Boolean
*/

/*
//可遍历的类型
const mapTag = 'Map'
const setTag = 'Set'
const arrTag = 'Array'
const objTag = 'Object'
const deepTag = [
  mapTag,
  setTag,
  arrTag,
  objTag,
]

//不可遍历的类型
const boolTag = 'Boolean'
const numTag = 'Number'
const strTag = 'String'
const errTag = 'Error'
const dateTag = 'Date'
const regTag = 'RegEpx'
const symTag = 'Symbol'
const funTag = 'Function'


//克隆Symbol
function cloneSymbol(target) {
  return Object(Symbol.prototype.valueOf.call(target))
}
//克隆正则
function cloneReg(target) {
  const reFlags = /\w+$/
  const res = new target.constructor(target.source, reFlags.exec(target))
  res.lastIndex = target.lastIndex
  return res
}
//不可遍历的类型，Boolean、Number、String、Date、Error，可以直接用构造函数和原始数据进行创建
function cloneOtherType(target, type) {
  const make = target.constructor;
  switch (type) {
    case boolTag:
    case numTag:
    case strTag:
    case errTag:
    case dateTag:
      return new make(target)
    case regTag:
      return cloneReg(target)
    case symTag:
      return cloneSymbol(target)
    case funTag:
      return cloneFun(target)
    default:
      return null
  }
}

//初始化
function init(target) {
  return new target.constructor()
}

//克隆函数，有两种情况，一个是普通函数，一个箭头函数
function cloneFun(target){
  const bodyReg=/(?<={)(.|\n)?(?=})/m
  const parmaReg=/(?<=\().+(?=\)\s+{)/
  const funStr=target.toString()
  if(target.prototype){//普通函数
    const body=bodyReg.exec(funStr)
    const param=parmaReg.exec(funStr)
    const paramArr=param?param[0].split(','):[]
    if(body){
      if(param){
        
        return new Function(...paramArr,body[0])
      }else{
        return new Function(body[0])
      }
    }else{
      return new Function(...paramArr)
    }
  }else{//箭头函数
    return eval(funStr)
  }

}

function deepClone(data, map = new WeakMap()) {
  //原始数据直接返回
  if (!isObject(data)) return data

  //初始化
  const type = getType(data)
  let target
  if (deepTag.includes(type)) {
    target = init(data)
  }

  if (map.has(data)) return map.get(data) //如果map中存有则取出返回,防止循环递归
  map.set(data, target) //设置缓存

  //克隆set
  if (type === setTag) {
    data.forEach(val => target.add(deepClone(val, map)))
    return target
  }

  //克隆map
  if (type === mapTag) {
    data.forEach((val, key) => target.set(key, deepClone(val, map)))
    return target
  }

  //克隆对象和数组
  if (type === objTag || type === arrTag) {
    const keys = type === arrTag ? undefined : Object.keys(data)
    for (let i = 0; i < (keys ? keys.length : data.length); i++) {
      let key = keys ? keys[i] : i
      target[key] = deepClone(data[key], map)
    }
    return target
  }

  //克隆剩余的类型
  return cloneOtherType(data, type)
}
*/

/*
const p1 = {
  name: '张三',
  age: 31,
  say: () => {},
  a: /abc/g,
  b: Date.now(),
  c: Error(123),
  f: () => {},
  e:function(a,b,c){},
  g:function(){},
  children: [{
      name: '张四',
      age: 11,
      say: () => {}
    },
    {
      name: '张五',
      age: 9,
      say: () => {}
    },
  ],
  info: {
    address: 'xxxxx-x-x-xx-x-',
    money: '1656131',
    height: '1.76',
    house: [{
        site: 'xxx-xx-xxx'
      },
      {
        site: 'xxx-xx-xxx'
      },
      {
        site: 'xxx-xx-xxx'
      }
    ]
  }
}
*/
// const p2 = deepClone(p1)



// p2.children[0].name = '王四'

// console.log(p2)
// console.log(p2.children[0].name)
// console.log(p1.children[0].name)
/*
const p3 = {
  a: /abc/g,
  b: Date.now(),
  c: Error(123),
  f: () => {},
  r: function(a,b,c) {console.log(a,b,c)},
  x: function() {},
}
p3.d = p3

p4 = deepClone(p3)
console.log(p4)
*/


//可遍历
const mapTag='Map'
const setTag='Set'
const arrTag='Array'
const objTag='Object'
const deepTag=[
  mapTag,
  setTag,
  arrTag,
  objTag,
]

//不可遍历
const numTag='Number'
const strTag='String'
const booTag='Boolean'
const symTag='Symbol'
const regTag='RegExp'
const dateTag='Date'
const errTag='Error'
const funTag='Function'

function isObject(target){
  return (typeof target==='object'&&target!==null)||typeof target==='function'
}

function getType(target){
  return Object.prototype.toString.call(target).slice(8,-1)
}

function getInit(target){
  return new target.constructor()
}

function cloneSym(target){
  return Object(Symbol.prototype.valueOf.call(target))
}

function cloneReg(target){
  const reFlags=/\w+$/
  const res=new target.constructor(target.source,reFlags.exec(target))
  res.lastIndex=target.lastIndex
  return res
}

function cloneFun(target){
  const funStr=target.toString()
  const bodyReg=/(?<={)(.|\n)+(?=})/
  const paramReg=/(?<=\().+(?=\)\s+)/
  const param=paramReg.exec(funStr)
  const paramArr=param?param[0].split(','):[]
  if(target.prototype){//普通函数
    const body=bodyReg.exec(funStr)
    if(body){
      console.log(paramArr)
      return new Function(...paramArr,body[0])
    }else{
      return new Function(...paramArr)
    }
  }else{//箭头函数
    return eval(funStr)
  }
}

function cloneOtherType(target,type){
  const maker=target.constructor
  switch(type){
    case numTag:
    case strTag:
    case booTag:
    case errTag:
    case dateTag:
      return new maker(target);
    case symTag:
      return cloneSym(target);
    case regTag:
      return cloneReg(target);
    case funTag:
      return cloneFun(target)
  }
}


function deepClone(data,map=new Map()){
  //原始类型则返回
  if(!isObject(data))return data

  const type=getType(data)
  let target;
  if(deepTag.includes(type))target=getInit(data)
  //map防止循环引用
  if(map.has(data))return map.get(data)
  map.set(data,target)

  //克隆set
  if(type===setTag){
    data.forEach(val=>target.add(val))
    return target
  }

  //克隆map
  if(type===mapTag){
    data.forEach((val,key)=>{
      target.set(key,val)
    })
    return target
  }

  //克隆普通对象和数组
  if(type===arrTag||type===objTag){
    let keys=type===objTag?Object.keys(data):null
    for (let i = 0; i < (keys?keys.length:data.length); i++) {
      let key=keys?keys[i]:i
      if(isObject(data[key])){
        target[key]=deepClone(data[key],map)
      }else{
        target[key]=data[key]
      }
    }
    return target
  }

  //克隆其它类型
  return cloneOtherType(data,type)
}



const p3 = {
  a: /abc/g,
  b: Date.now(),
  c: Error(123),
  f: () =>{},
  r: function(a,b,c) {console.log(a,b,c)},
  x: function() {},
}
p3.d = p3

p4 = deepClone(p3)
console.log(p4)